/*
   Copyright 2009 Abdulla Abdurakhmanov (abdulla@latestbit.com)
   Original sources are available at www.latestbit.com

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

       http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.
*/

package org.latestbit.smartobjects.network;

import java.io.IOException;
import java.util.*;

public class StdFailOverCoordinator implements IEndpointCoordinator {
	public final static String COORDINATOR_NAME = "StdFailOverCoordinator";
	
	protected HashMap<IManagedConnection, Set<ConnectionHandler>> connections = new HashMap<IManagedConnection, Set<ConnectionHandler>>();
	protected HashMap<ConnectionHandler, IManagedConnection> handlers = new HashMap<ConnectionHandler, IManagedConnection>();
	
	protected IManagedConnectionFactory factory;
	
	protected synchronized ManagedConnectionStatus checkConnectionStatus(IManagedConnection connection) {
		if(connections.get(connection)!=null) {
			return ManagedConnectionStatus.CONNECTED;
		}
		else
			return ManagedConnectionStatus.DISCONNECTED;
	}

	protected synchronized void insertHandler(IManagedConnection connection, ConnectionHandler handler) {
		Set<ConnectionHandler> handlersPerConnection = connections.get(connection);
		if(handlersPerConnection==null) {
			handlersPerConnection = new HashSet<ConnectionHandler>();
			connections.put(connection, handlersPerConnection);
		}
		handlersPerConnection.add(handler);
		handlers.put(handler, connection);
	}
	
	protected synchronized void removeHandler(IManagedConnection connection, ConnectionHandler handler) {
		Set<ConnectionHandler> handlersPerConnection = connections.get(connection);
		if(handlersPerConnection!=null) {
			handlersPerConnection.remove(handler);
			if(handlersPerConnection.isEmpty()) {
				connections.remove(connection);
			}
		}
		handlers.remove(handler);
	}
	
	@Override
	public synchronized ManagedConnectionStatus onConnected(ConnectionHandler handler) {
		if(hasManagedConnectionFactory()) {
			IManagedConnection connection = factory.createManagedConnection();
			insertHandler(connection, handler);
			connection.assignConnectionHandler(handler);
			return checkConnectionStatus(connection);
		}
		else
			return ManagedConnectionStatus.DISCONNECTED;
	}
	

	@Override
	public synchronized ManagedConnectionStatus onConnectionFailure(ConnectionHandler handler,
			String errorMessage) {
		IManagedConnection connection = handlers.get(handler);
		if(connection!=null) {
			return checkConnectionStatus(connection);
		}
		else
			return ManagedConnectionStatus.DISCONNECTED;
	}

	@Override
	public synchronized ManagedConnectionStatus onDisconnected(ConnectionHandler handler) {
		IManagedConnection connection = handlers.get(handler);
		if(connection!=null) {
			connection.unassignConnectionHandler(handler);
			removeHandler(connection, handler);			
			return checkConnectionStatus(connection);
		}
		else
			return ManagedConnectionStatus.DISCONNECTED;
	}

	@Override
	public synchronized ManagedConnectionStatus onRemoved(ConnectionHandler handler) {
		return onDisconnected(handler);
	}

	@Override
	public synchronized boolean onRestoreConnectionNeeded(ConnectionHandler handler) {
		return true;
	}

	@Override
	public synchronized ConnectionHandler getConnectionHandlerToSend (IManagedConnection managedConnection ) throws IOException {
		Set<ConnectionHandler> handlersPerConnection = connections.get(managedConnection);
		if(handlersPerConnection!=null) {
			ConnectionHandler handler = handlersPerConnection.iterator().next();
			return handler;
		}
		else
			throw new IOException("Unable to found any available connections to send");
	}

	@Override
	public synchronized IManagedConnectionFactory getManagedConnectionFactory() {
		return factory;
	}

	@Override
	public synchronized void setManagedConnectionFactory(IManagedConnectionFactory factory) {
		this.factory = factory;
	}
	
	public boolean hasManagedConnectionFactory() {
		return this.factory != null;
	}

	@Override
	public String getName() {
		return COORDINATOR_NAME;
	}

}
